gl backend: Avoid roundtripping via surface when updloading
authorAlexander Larsson <alexl@redhat.com>
Thu, 24 Sep 2020 14:58:46 +0000 (16:58 +0200)
committerAlexander Larsson <alexl@redhat.com>
Fri, 25 Sep 2020 08:04:48 +0000 (10:04 +0200)
Do custom uploads rather than using gdk_cairo_surface_upload_to_gl(),
because this way we avoids a roundtrip (memcpy and possibly conversion)
to the cairo image surface format.

gdk/gdkmemorytexture.c
gdk/gdkmemorytextureprivate.h
gsk/gl/gskgldriver.c
gsk/gl/gskgldriverprivate.h
gsk/gl/gskglrenderer.c

index 5ed0b988dc3389c91d63ef9991bfe30b4cbfd92a..663d0409f9ed40be93fd2923af16d5ef748b314b 100644 (file)
@@ -38,7 +38,7 @@ struct _GdkMemoryTextureClass
 
 G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
 
-static gsize
+gsize
 gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
 {
   switch (format)
index caa4b2014c4dcb14ad96061fcb9ca7176adcceed..740a59da6e6beba00c6b406be690f76a3584d0bb 100644 (file)
@@ -31,6 +31,8 @@ G_BEGIN_DECLS
 
 #define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_DEFAULT
 
+gsize                   gdk_memory_format_bytes_per_pixel   (GdkMemoryFormat    format);
+
 GdkMemoryFormat         gdk_memory_texture_get_format       (GdkMemoryTexture  *self);
 const guchar *          gdk_memory_texture_get_data         (GdkMemoryTexture  *self);
 gsize                   gdk_memory_texture_get_stride       (GdkMemoryTexture  *self);
index 8d101b93df504f6fc68163adbe07bdc107b90118..a383344eaefe181d13c8e14176ca3e3fa1023fa2 100644 (file)
@@ -7,6 +7,7 @@
 #include "gdk/gdkglcontextprivate.h"
 #include "gdk/gdktextureprivate.h"
 #include "gdk/gdkgltextureprivate.h"
+#include "gdkmemorytextureprivate.h"
 
 #include <gdk/gdk.h>
 #include <epoxy/gl.h>
@@ -58,6 +59,54 @@ struct _GskGLDriver
 
 G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
 
+static void
+upload_gdk_texture (GdkTexture      *source_texture,
+                    int              target,
+                    int              x_offset,
+                    int              y_offset,
+                    int              width,
+                    int              height)
+{
+  cairo_surface_t *surface = NULL;
+  GdkMemoryFormat data_format;
+  const guchar *data;
+  gsize data_stride;
+  gsize bpp;
+
+  g_return_if_fail (source_texture != NULL);
+  g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture));
+  g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture));
+
+  /* Note: GdkGLTextures are already handled before we reach this and reused as-is */
+
+  if (GDK_IS_MEMORY_TEXTURE (source_texture))
+    {
+      GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture);
+      data = gdk_memory_texture_get_data (memory_texture);
+      data_format = gdk_memory_texture_get_format (memory_texture);
+      data_stride = gdk_memory_texture_get_stride (memory_texture);
+    }
+  else
+    {
+      /* Fall back to downloading to a surface */
+      surface = gdk_texture_download_surface (source_texture);
+      cairo_surface_flush (surface);
+      data = cairo_image_surface_get_data (surface);
+      data_format = GDK_MEMORY_DEFAULT;
+      data_stride = cairo_image_surface_get_stride (surface);
+    }
+
+  bpp = gdk_memory_format_bytes_per_pixel (data_format);
+
+  gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
+                                 data + x_offset * bpp + y_offset * data_stride,
+                                 width, height, data_stride,
+                                 data_format, target);
+
+  if (surface)
+    cairo_surface_destroy (surface);
+}
+
 static Texture *
 texture_new (void)
 {
@@ -408,32 +457,15 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
 
   slices = g_new0 (TextureSlice, cols * rows);
 
-  /* TODO: (Perf):
-   *   We still create a surface here, which should obviously be unnecessary
-   *   and we should eventually remove it and upload the data directly.
-   */
   for (col = 0; col < cols; col ++)
     {
       const int slice_width = MIN (max_texture_size, texture->width - x);
-      const int stride = slice_width * 4;
 
       for (row = 0; row < rows; row ++)
         {
           const int slice_height = MIN (max_texture_size, texture->height - y);
           const int slice_index = (col * rows) + row;
-          guchar *data;
           guint texture_id;
-          cairo_surface_t *surface;
-
-          data = g_malloc (sizeof (guchar) * stride * slice_height);
-
-          gdk_texture_download_area (texture,
-                                     &(GdkRectangle){x, y, slice_width, slice_height},
-                                     data, stride);
-          surface = cairo_image_surface_create_for_data (data,
-                                                         CAIRO_FORMAT_ARGB32,
-                                                         slice_width, slice_height,
-                                                         stride);
 
           glGenTextures (1, &texture_id);
 
@@ -442,7 +474,7 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
 #endif
           glBindTexture (GL_TEXTURE_2D, texture_id);
           gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST);
-          gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, slice_width, slice_height, NULL);
+          upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height);
 
 #ifdef G_ENABLE_DEBUG
           gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
@@ -451,9 +483,6 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
           slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height};
           slices[slice_index].texture_id = texture_id;
 
-          g_free (data);
-          cairo_surface_destroy (surface);
-
           y += slice_height;
         }
 
@@ -486,7 +515,8 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
                                        int          mag_filter)
 {
   Texture *t;
-  cairo_surface_t *surface;
+  GdkTexture *downloaded_texture = NULL;
+  GdkTexture *source_texture;
 
   if (GDK_IS_GL_TEXTURE (texture))
     {
@@ -494,14 +524,20 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
 
       if (texture_context != self->gl_context)
         {
+          cairo_surface_t *surface;
+
           /* In this case, we have to temporarily make the texture's context the current one,
            * download its data into our context and then create a texture from it. */
           if (texture_context)
             gdk_gl_context_make_current (texture_context);
 
           surface = gdk_texture_download_surface (texture);
+          downloaded_texture = gdk_texture_new_for_surface (surface);
+          cairo_surface_destroy (surface);
 
           gdk_gl_context_make_current (self->gl_context);
+
+          source_texture = downloaded_texture;
         }
       else
         {
@@ -519,7 +555,7 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
             return t->texture_id;
         }
 
-      surface = gdk_texture_download_surface (texture);
+      source_texture = texture;
     }
 
   t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
@@ -528,15 +564,16 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
     t->user = texture;
 
   gsk_gl_driver_bind_source_texture (self, t->texture_id);
-  gsk_gl_driver_init_texture_with_surface (self,
-                                           t->texture_id,
-                                           surface,
-                                           min_filter,
-                                           mag_filter);
+  gsk_gl_driver_init_texture (self,
+                              t->texture_id,
+                              source_texture,
+                              min_filter,
+                              mag_filter);
   gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
                                       "GdkTexture<%p> %d", texture, t->texture_id);
 
-  cairo_surface_destroy (surface);
+  if (downloaded_texture)
+    g_object_unref (downloaded_texture);
 
   return t->texture_id;
 }
@@ -769,11 +806,11 @@ filter_uses_mipmaps (int filter)
 }
 
 void
-gsk_gl_driver_init_texture_with_surface (GskGLDriver     *self,
-                                         int              texture_id,
-                                         cairo_surface_t *surface,
-                                         int              min_filter,
-                                         int              mag_filter)
+gsk_gl_driver_init_texture (GskGLDriver     *self,
+                            int              texture_id,
+                            GdkTexture      *texture,
+                            int              min_filter,
+                            int              mag_filter)
 {
   Texture *t;
 
@@ -794,7 +831,7 @@ gsk_gl_driver_init_texture_with_surface (GskGLDriver     *self,
 
   gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
 
-  gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, t->width, t->height, NULL);
+  upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height);
 
 #ifdef G_ENABLE_DEBUG
   gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
index 406b95937283fda0d939a6ecba2b6d7fe483730e..0bf9ca89d281570a846d5f9fce4ee836b45aeb10 100644 (file)
@@ -63,9 +63,9 @@ void            gsk_gl_driver_init_texture_empty        (GskGLDriver     *driver
                                                          int              texture_id,
                                                          int              min_filter,
                                                          int              max_filter);
-void            gsk_gl_driver_init_texture_with_surface (GskGLDriver     *driver,
+void            gsk_gl_driver_init_texture              (GskGLDriver     *driver,
                                                          int              texture_id,
-                                                         cairo_surface_t *surface,
+                                                         GdkTexture      *texture,
                                                          int              min_filter,
                                                          int              mag_filter);
 
index 96aecbd70bd09bcae6cc392174deb12b4e1b9bb1..7998cbfd34b4b5b804bd7520650036efc530cc86 100644 (file)
@@ -564,6 +564,7 @@ render_fallback_node (GskGLRenderer   *self,
                       GskRenderNode   *node,
                       RenderOpBuilder *builder)
 {
+  GdkTexture *texture;
   const float scale = ops_get_scale (builder);
   const int surface_width = ceilf (node->bounds.size.width * scale);
   const int surface_height = ceilf (node->bounds.size.height * scale);
@@ -645,15 +646,18 @@ render_fallback_node (GskGLRenderer   *self,
 #endif
   cairo_destroy (cr);
 
+
   /* Upload the Cairo surface to a GL texture */
   texture_id = gsk_gl_driver_create_texture (self->gl_driver,
                                              surface_width,
                                              surface_height);
   gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
-  gsk_gl_driver_init_texture_with_surface (self->gl_driver,
-                                           texture_id,
-                                           surface,
-                                           GL_NEAREST, GL_NEAREST);
+
+  texture = gdk_texture_new_for_surface (surface);
+  gsk_gl_driver_init_texture (self->gl_driver,
+                              texture_id,
+                              texture,
+                              GL_NEAREST, GL_NEAREST);
 
   if (gdk_gl_context_has_debug (self->gl_context))
     gdk_gl_context_label_object_printf  (self->gl_context, GL_TEXTURE, texture_id,
@@ -661,6 +665,7 @@ render_fallback_node (GskGLRenderer   *self,
                                          g_type_name_from_instance ((GTypeInstance *) node),
                                          texture_id);
 
+  g_object_unref (texture);
   cairo_surface_destroy (surface);
   cairo_surface_destroy (rendered_surface);